TECHNOTE 1094Virtual Memory Application CompatibilityApple Developer Technical Support |
Although Virtual Memory (VM) has been available on the Mac OS since the release of System 7, its use has become more widespread in recent years. This is because of a combination of factors, including
Because VM is so widely used, it's critically important to make sure that your software is compatible with it. This Technote, which is divided into two parts, provides you with
All Mac OS programmers should read this Note. For application developers, this Note supplants and extends the information presented in Technote ME 09, "Coping With VM and Memory Mappings". Device driver writers should continue to refer to Technote ME 09.
|
Change history:
1.0 |
03/97 |
initial version |
1.1 |
3/98 |
updated to reference the Mac OS 8.1 VM paging control |
VM Compatibility ChecklistIn general, programs should be able to ignore VM and operate successfully. Specific problems may occur under the following conditions:
If your program does none of the operations listed, it should already be VM-compatible. If you want to improve your code's performance under VM, you should read the section Ways to Improve Performance in this Note. On the other hand, if your program does any of these operations, you'll want to read the next section, Programming Implications, which explains how to make your program VM-compatible. That section is also a good place to start if your program is incompatible with VM and all you want to do is fix it as quickly as possible. For an overview of VM theory, terminology, and implementation under System 7, you'll want to go to the How VM Works section. |
Programming ImplicationsIf your program does any of the things listed above, you should read the next few sections for information on how to execute safely under VM. |
Direct SCSI Manager or ATA Manager CallsSome applications, such as scanning programs or hard disk formatters, need to call the SCSI or ATA Managers directly. These applications must take special care when VM is enabled. For example, if such an application was to grab exclusive access to the SCSI bus and then take a page fault, VM would be unable to swap in the new page, making the page fault fatal. The solution is simple: if your program calls the SCSI or ATA Manager directly, you must make sure that any code executed or data accessed while paging is unsafe is held in memory. If you're writing an application, meeting that requirement can be tricky because it's hard to hold just part of an application's code in memory. For this reason, your application might want to implement this functionality as a bundled device driver or other code resource. This separation makes it easy for the application to ensure that the code running while paging is unsafe is held. |
Hardware Interrupt Handlers and Physical AddressingSome applications need to drive hardware devices
directly; for example, a data capture program that talks to
a dedicated PCI card. Such applications typically install
hardware interrupt handlers (either using
The basic rules for device driver writers are:
For further explanation, see the above references. As an aside, in future versions of the Mac OS it is likely that these functions will be privileged and inaccessible from applications. If your application does this sort of thing, it is most probably a good idea to prepare for the future by factoring your code into an application and a bundled device driver. |
AppleTalk Socket ListenersAppleTalk socket listeners are also not VM-safe. An AppleTalk socket listener on a slow computer using LocalTalk networking has very difficult real-time goals. Deferring this while waiting for a page fault would cause serious packet loss. So, for performance reasons, VM does not defer socket listeners until paging is safe. Socket listeners must be written so that they don't cause a page fault. The way to make your socket listener VM-safe is to ensure
that all code that can be called by the socket listener, and
the data it accesses, is either held in memory or deferred
using |
Calling Non-Interrupt-Safe RoutinesIf your code calls routines that are not interrupt-safe while handling a non-deferred hardware interrupt (or, for that matter, any time paging is unsafe), you may encounter a problem running on Mac OS 7.6 and future releases of the Mac OS that use System 7-style VM. In older Mac OS implementations, large parts of system software were in the system heap simply so that they could be shared between applications. These parts caused the system heap to grow in size. Because the system heap is always held, these parts were resident, even though they didn't need to be. This reduced the number of physical pages of RAM that VM had available to use as a cache for its various logical address ranges. This reduction made the system slower. Under versions of the Mac OS that use System 7-style VM, these system parts are being moved out of the system heap and into file-mapped CFM containers. This makes them available for paging, and increases the number of physical pages available to VM. In general, this helps system performance. Such parts of the system are only made pageable if none of their routines can be called at interrupt time. Otherwise, a non-deferred hardware interrupt might call these routines and cause a fatal page fault. However, some programs call non-interrupt-safe routines when paging isn't safe. This works under earlier versions of Mac OS because the code for these routines was in the system heap, and hence resident. Such software has problems under newer versions of Mac OS when VM is turned on because these routines are no longer held. If your software was previously compatible with VM and broke under Mac OS 7.6, you should check to be sure that you are not calling non-interrupt-safe routines when paging is unsafe. |
Switching Stacks at Interrupt TimeIf your code switches stacks at interrupt time (usually this is done to guarantee a minimum amount of available stack space), it must ensure that the stack is held. This is true even if your code runs at times when paging is normally safe, such as deferred task time. For a detailed explanation of why this is necessary, see User vs Supervisor Mode. |
Ways to Improve PerformanceThis section describes a number of things that you can do to make your application work better under VM. Grouping Commonly Used Code and DataPerhaps the best thing you can do to make your application work better under VM is to analyze its working set. The working set of a program is the set of memory pages that the program accesses most often. The smaller you make your working set, the better your program will run under VM. If the working set of all the active processes exceeds the amount of physical memory available for paging, the system begins to take an excessive number of page faults. This state is known as thrashing. Under System 7 there are no good tools for analyzing your application's working set automatically. However, you can do some things to empirically adjust your working set. One good thing is to 'segment' your CFM-based applications sensibly. Most development environments provide a mechanism for CFM-based applications to sort routines in their PEF container according to a "group" that is set using compiler directives. This is analogous to the classic runtime segmentation model. You can use these directives to group rarely used functions together and away from the commonly used functions. This helps keep rarely used code paged out, which reduces your working set. |
Sensible Memory ManagementAnother good place to look when analyzing your working set is your memory management system. Some memory management systems are VM-friendly, and some are not. For example, if your memory management system looks at every block in the heap when a block is freed, it has pathologically bad VM performance. On PowerPC machines, the system Memory Manager (also known as the Modern Memory Manager) has been optimized to be as VM-friendly as possible. It's important that you be compatible with the Modern Memory Manager, for this and other reasons. On 680x0 machines, the Memory Manager has some behaviors that cause excessive page faults under VM. Unfortunately, there's not a lot you can do about the system Memory Manager on these machines other than avoid using it. If you're using your own memory management scheme (for C++ objects, for example), you should look at its implementation to determine whether it's VM-friendly. A VM-friendly memory manager attempts to reduce the number of times it looks at bytes located in different pages, and thus minimizes the program's working set. You may be able to switch memory managers and get VM performance benefits. Another way to avoid thrashing is to minimize your use of Process Manager temporary memory. While it may appear that this memory is free for application use, under VM this memory has often been paged out, and therefore is not 'real'. So if you look at temporary memory and see that there's a huge amount of free space, do not allocate and use it all. This will likely cause the system to thrash. Only allocate as much temporary memory as you actually need. |
Paging Control APIMac OS 8.1 introduced a new API for the Virtual Memory Manager, the VM paging control API. Your application can use this API to advise VM on how it is using memory. This allows VM to optimize its paging behavior to improve application, and overall system, performance. You can read more about the VM paging control API in the Mac OS 8.1 Technote. |
Judicious Use of
|
Emulated InstructionsWhen you execute a privileged 680x0 instruction, the Virtual Memory Manager emulates that instruction to prevent a privilege violation exception. This emulation takes time. You should avoid executing privileged 680x0 instructions where possible. This list of these instructions is given in the section Privileged Instruction Emulation. One common use of emulated instructions is disabling
interrupts to ensure atomicity. In a lot of cases, you can
avoid this by using the processor's built-in synchronization
primitives. This includes the
|
VM and Interrupt LatencyIf interrupt latency is important to you, VM presents further challenges. Because VM defers user code until page faults are no longer fatal, it can increase the latency between when the interrupt is signalled and when the user code executes. There are some things you can do to reduce specific interrupt latency problems when running under VM:
Both techniques employ the same basic idea, which is that you deliberately take some of your user code and turn it into non-user code to avoid VM's latency. |
Some VM WeirdnessesThis section describes a few little things about VM -- its weirdnesses -- that you should know about. System Heap is HeldUnder Apple's current VM implementation, the entire system heap is automatically held resident in physical memory. This is done to provide compatibility with old device drivers that would not otherwise be compatible with VM. There may be future Apple VM implementations (or current non-Apple VM implementations) that do not hold the entire system heap. Thus, you should not rely on this safety net. If you want something to be held, you should explicitly hold it. Holding Memory at Interrupt TimeThe VM API routines for holding memory resident
(
These routines can cause paging activity as VM swaps in the memory that is to be held. Obviously, this is bad when paging is unsafe. So you must either ensure that paging is safe or that the call won't cause any paging activity. Holding Memory with Interrupts DisabledOn computers running system software prior to System
7.1.2, VM specifically prohibited all routines based on the
The typical reason for calling Open and Close AsynchronousTechnote ME 09,"Coping With VM and Memory Mappings" says:
This statement is erroneous in one respect. VM does not
patch What Form of Address to Pass?All of the VM API routines (
Allocating Memory Above
|
Things to Do in MacsBug When You're DeadWhen, after reading through this Note and checking that you follow all the rules given here, you still find that your software crashes when VM is enabled, there are things you can do to determine what went wrong. The most common symptom of failure under VM is that you drop into MacsBug with a bus error because VM was unable to satisfy a page fault request. Remember that VM is hooked into the bus error handler and propagates any bus errors that it's unable to handle -- either because it's not in a section of memory that's under VM control, or because it's already handling a page fault, or for any other reason -- to the bus error handler pointed to by the low memory pseudo-vector. There are a number of things you can do to recognize and analyze this situation listed in the following sections. Recognizing a Fatal Page FaultThe most obvious symptom of a fatal page fault is that you end up in MacsBug with a bus error. Unfortunately bus errors have more than one cause. You can a bus error because of a fatal page fault, but the most common cause for bus errors is dereferencing a bogus pointer. You can quickly see whether this bus error is a possible fatal page fault by checking whether paging is safe. If you look on the left side of the MacsBug display, you will see a two character code that describes the state of VM. If this code is "RM" (Real Memory), VM is not running. If this state is "VM", VM is running but paging is safe. If the state is "vM", VM is running and paging is not safe. If paging is safe, the bus error is not a fatal page fault, and you should look elsewhere for the cause. Of course, not all bus errors that happen when paging is unsafe are fatal page faults. It takes a bit more work to determine this. For example, imagine dropping into MacsBug with the following message: Bus Error accessing 00123456 at PC 4080BB8C 4080BB8C MOVE.W $0010(A0),D0 MacsBug reports that the instruction that caused the bus error is at address $4080BB8C, but this report is not always correct. The dynamic recompiling (DR) emulator found on recent PowerPC computers can cause an imprecise PC address to be reported by MacsBug. However, one piece of information that you can rely on is the address that was being accessed and caused the page fault. In the above example, this is address $00123456. You can find out information about this address using the following MacsBug command: dm $00123456 wh $00123456 If this command indicates that the address is in valid memory (i.e., either the primary address range or one of the file mapped address ranges), the access should have succeeded. The only reason for this bus error is a fatal page fault. If the Another common form of fatal page fault is reported as: Bus Error at 4080BB8C while writing long work (data = 0000009B) to 00123456 4080BB8C LINK A6,#$FFCC The A third form of fatal page fault is reported as: Bus Error accessing 00123456 at PC 00123456 Unable to access that address In this case, the actual instruction fetch has caused a
bus error. You can confirm that this is a fatal page fault
by looking to see whether paging is safe and by checking the
target address using
Is This a Double Page Fault?Not all fatal page faults are double page faults. For example, if you take a page fault while the device driver that's controlled the backing store is busy, the page fault is fatal, even though it isn't a double page fault. See the "Preventing Fatal Page Faults" section for a description of the various reasons why a page fault might be fatal. Determining whether a fatal page fault is a double page fault is reasonably tricky. One good indicator is whether you're in user or supervisor mode. You can tell this by looking at the S bit in the SR display in MacsBug. If this is a capital "S", you are in supervisor mode; if this is a lower case "s" you are in user mode. You can't get double page faults from user mode. If you
get a fatal page fault and you're in user mode, you know
there was some other cause. One good place to start
debugging this is to use MacsBug's Unfortunately, taking a fatal page fault in supervisor
mode still isn't a guarantee that it was a double page fault.
The only certain way to determine whether it was a double
page fault is to dump memory starting at the
ISP and
look for a
bus
error exception stack frame on the interrupt stack. Bus
error exception stack frames are relatively easy to
recognize because they contain a special word that denotes
the frame type. For a bus errror on a 68040, this value is
You can also use other fields in the exception frame to confirm that you have found the correct frame. All of the exception stack frames we're interested in share a common format, as shown below:
Once you have found the frame type word on the stack, you
can look back six bytes to find the value of the Status
Register (SR) immediately prior to the bus error. Common
values for SR are Once you find the bus error exception stack frame, you can use the information in M68000 Family Programmer's Reference Manual to examine the frame and find more clues about the cause.
What Was the First Page Fault?Once you know you've taken a double page fault, you can find out information about the first page fault by looking at the bus error exception stack frame. The format of this frame is described in the M68000 Family Programmer's Reference Manual. For example, a bus error exception stack frame on a 68040 looks like:
Once you find the start of the frame, you can dump the
long at offset You can also dump the long at offset Finally, you can dump the word at offset Read-Only Memory ExceptionsWhen VM is enabled, it maps CFM containers into file mapping space. These file-mapped address ranges are read-only. Any attempt to write to your own code will cause you to drop into MacsBug with the message: PowerPC read-only memory exception at 02314BB8 main+00018 For example, the following snippet of PowerPC code runs just fine when VM is disabled, but dies under VM: static void Wibble(void) { } void main(void) { char x; x = **((char **) Wibble); **((char **) Wibble) = x; }
New System ErrorsSystem 7.5.5 introduced two new system errors related to virtual memory. Both errors are completely fatal for the system, but if you encounter one while debugging, it is useful to know their cause.
See Technote 1069 - "System 7.5.5" for more details on all of the changes that occurred in the System 7.5.5 release of VM. MacsBug
|
SummaryVirtual memory is not rocket science. While its implementation on the Mac OS is complicated by the constraints of the original design, it's not incomprehensible. Understanding how VM works will help you know why the rules are important, recognize when you need to apply them, and debug problems when they arise. |
|
Acrobat version of this Note (56K)
Thanks to Jim Luther, Eric Anderson, Bo3b Johnson, and Jeff Robbins.